
import java.awt.Point;
import java.util.*;

/**
 * The Model part of the application. Does all the work behind the {@link View}.<p>
 * Contains the main driver method.
 * 
 * @author Alex Mackay
 */
public class Model {
	
	private Room room = new Room(new Table(new Deck("PlayingCards")));
	private View view;
	private Set<Card> selectedCards = new HashSet<Card>(); // null indicates no selected card
	private Point prevPoint = new Point();
	
	/**
	* The main driver of the program.
	*/
	public static void main(String[] args) {
		Model m = new Model();
		View v = new View(m);
		m.setView(v);
	}
	
	/**
	 * Constructs a <code>Model</code> object.
	 */
	public Model() {}	
	
	/**
	 * Sets the {@link View} object for this <code>Model</code>.
	 * 
	 * @param view The <code>View</view> object to set.
	 */
	public void setView(View view) {
		this.view = view;
	}	
	
	public boolean updateView() {
		if (view==null) {
			return false;
		}
		view.repaint();
		return true;
	}
	
	/**
	* Set a {@link Card} as the only selected card.
	* 
	* @param card The <code>Card</code> to be selected.
	*/
	public boolean selectSingleCard(Card card) /*throws IllegalArgumentException*/ {
		deselectAllCards();
		boolean val = selectCard(card);
		updateView();
		return val;
	}
	
	/**
	* Add a {@link Card} to those selected.
	* 
	* @param card The <code>Card</code> to be selected.
	*/
	public boolean selectCard(Card card) /*throws IllegalArgumentException*/ {
		if (card==null) {
			// uncomment the following line once exception handling is implemented in the View
			//throw IllegalArgumentException("card cannot be null");
			return false;
		}
		System.out.println("selectCard - " + card.toString());
		
		selectedCards.add(card);
		updateView();
		return false;
	}
	
	/**
	* Remove a {@link Card} from those selected.
	* 
	* @param card The <code>Card</code> to be deselected.
	*/
	public void deselectCard(Card card) {
		if (card==null) {
			// uncomment the following line once exception handling is implemented in the View
			//throw IllegalArgumentException("card cannot be null");
		}
		selectedCards.remove(card);
		updateView();
	}
	
	public void toggleSelectCard(Card card) {
		if (card==null) {
			// uncomment the following line once exception handling is implemented in the View
			//throw IllegalArgumentException("card cannot be null");
		}
		if (isSelectedCard(card)) {
			deselectCard(card);
		} else {
			selectCard(card);
		}
		updateView();
	}
	
	
	public void deselectAllCards() {
		selectedCards = new HashSet<Card>();
		updateView();
	}
	
	/**
	 * Flips the currently selected {@link Card}.
	 * 
	 * @return <code>true</code> if there currently is a selected <code>Card</code>, otherwise <code>false</code>.
	 */
	public boolean flipSelected(){
		
		System.out.println("Flip - " + selectedCards.toString());
		
		if (selectedCards.isEmpty()) {
			return false;
		}
		for (Card c: selectedCards) {
			c.toggleFaceUp();
		}
		updateView();
		return true;
	}
	
	/**
	 * Tests if a {@link Card} is currently selected.
	 * 
	 * @param card The <code>Card</code> to test.
	 * @return <code>true</code> if the specified <code>Card</code> is selected, otherwise <code>false</code>.
	 */
	public boolean isSelectedCard(Card card) {
		return selectedCards.contains(card);
	}
	
	/**
	 * Returns the currently selected {@link Card}.
	 * 
	 * @return the currently selected <code>Card</code>.
	 */
/*	public Card getSelected(){
		return selectedCard;
	}*/
	
	/**
	 * Calculates the highest Z-index of all cards intersecting with a given {@link Card}.
	 * 
	 * @param selected The selected <code>Card</code>.
	 * @return The highest Z-index found.
	 */
	private int findHighestZ(Card selected) {
		
		Card[] cards = getRoom().getTable().getDeck().getDeck();
		int highestZ = 0;
	
		for(Card card: cards){
			
			if(selected == card) continue;
			
			if(selected.getRectangle().intersects(card.getRectangle())){//intersects with point
				if(card.getZ() > highestZ){//check if highest z
					highestZ = card.getZ();//set c and z
				}
			}
		}
		
		System.out.println("z " + (highestZ + 1));
		return highestZ;
	}
	
	/**
	 * Records the starting <code>Point</code> of a mouse drag. Moves selected {@link Card} to the top of any cards it is touching. 
	 * 
	 * @param point The starting location of the drag.
	 */
	public void dragStart(Point point) {
		prevPoint = point;
		
		for (Card c: selectedCards) {
			//fix z
			c.setZ(findHighestZ(c) + 1);
		}
		updateView();
	}
	
	/**
	 * Called while the mouse is being dragged to update the position and Z-value of the selected {@link Card}.
	 * 
	 * @param point The current location of the mouse.
	 */
	public void dragContinue(Point point) {
		int dx = point.x - prevPoint.x;
		int dy = point.y - prevPoint.y;
		
		for (Card c: selectedCards) {
			Point oldLoc = c.getLocation();
			c.setLocation(new Point(oldLoc.x + dx, oldLoc.y + dy));
			prevPoint = point;
			
			// fix z
			c.setZ(findHighestZ(c) + 1);
		}
		
		updateView();
	}
	
	/**
	 * Ends a drag??
	 * 
	 * @param point **NOT USED**
	 */
	public void dragEnd(Point point) {
		
		updateView();
	}
	
	/**
	 * Returns the {@link Room}.
	 * 
	 * @return the <code>Room</code>.
	 */
	public Room getRoom() {
		return room;
	}	
}
